package com.lan.workflow.listener;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.google.common.base.Ascii;
import com.lan.workflow.common.TableNameEntityClassMapping;
import com.lan.workflow.util.SpringUtils;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.activiti.engine.impl.el.JuelExpression;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.time.LocalDateTime;


@Component("workFlowStatusListener")
public class WorkFlowStatusListener implements ExecutionListener {
    private static final long serialVersionUID = -8470466786519328818L;

    /**
     * 业务表中流程实例id字段
     */
    public static final String processInstanceId = "proc_inst_id";
    /**
     * 流业务表中程更新日期字段
     */
    public static final String wfStatusTime = "wf_status_time";


    private JuelExpression wf_status;

    private JuelExpression wf_back;

    /**
     * 流程实例id
     */
    private String processId;
    /**
     * 业务对象实例id
     */
    private String businessId;

    @Override
    public void notify(DelegateExecution deExecution) throws Exception {
        processId = deExecution.getProcessInstanceId();
        businessId = deExecution.getProcessBusinessKey();

        // 流程审批状态
        String status = "";
        if (wf_status != null) {
            status = wf_status.getExpressionText();
            if (StringUtils.isNotBlank(status)) {
                status = status.trim();
            }
        }

        if (deExecution instanceof ExecutionEntity) {//在分支线上处理流程状态
            ExecutionEntity execution = (ExecutionEntity) deExecution;

            ProcessDefinitionEntity entity = (ProcessDefinitionEntity) execution.getProcessDefinition();
            String tableName = entity.getCategory();
            if (StringUtils.isNotBlank(tableName)) {
                String entityClass = TableNameEntityClassMapping.mapping.get(tableName);
                Class aClass = Class.forName(entityClass);
                String tableIdFieldName = null;
                Class tableIdFieldType = null;
                Field[] declaredFields = aClass.getDeclaredFields();
                for(Field field : declaredFields){
                    TableId tableIdAnnotation = field.getAnnotation(TableId.class);
                    if(tableIdAnnotation != null){
                        tableIdFieldName = field.getName();
                        tableIdFieldType = field.getType();
                        break;
                    }
                }

                Object obj = aClass.newInstance();
                Field wfStatusField = aClass.getDeclaredField("wfStatus");
                wfStatusField.setAccessible(true);
                wfStatusField.set(obj, status);
                Field wfStatusTimeField = aClass.getDeclaredField("wfStatusTime");
                wfStatusTimeField.setAccessible(true);
                wfStatusTimeField.set(obj, LocalDateTime.now());

                StringBuffer mapperNameTempBuf = new StringBuffer(entityClass.substring(entityClass.lastIndexOf(".") + 1)).append("Mapper");
                StringBuffer mapperNameBuf = new StringBuffer(mapperNameTempBuf.length())
                        .append(Ascii.toLowerCase(mapperNameTempBuf.charAt(0))).append(mapperNameTempBuf.substring(1));
                BaseMapper baseMapper = SpringUtils.getBean(mapperNameBuf.toString());
                if(StringUtils.isNotBlank(tableIdFieldName)){
                    Field processInstanceIdField = aClass.getDeclaredField("procInstId");
                    processInstanceIdField.setAccessible(true);
                    processInstanceIdField.set(obj, processId);

                    Field tableIdField = aClass.getDeclaredField(tableIdFieldName);
                    tableIdField.setAccessible(true);
                    if(String.class.equals(tableIdFieldType)){
                        tableIdField.set(obj, businessId);
                    } else if(int.class.equals(tableIdFieldType) || Integer.class.equals(tableIdFieldType)){
                        tableIdField.set(obj, Integer.parseInt(businessId));
                    } else if(long.class.equals(tableIdFieldType) || Long.class.equals(tableIdFieldType)){
                        tableIdField.set(obj, Long.parseLong(businessId));
                    } else if(double.class.equals(tableIdFieldType) || Double.class.equals(tableIdFieldType)){
                        tableIdField.set(obj, Double.parseDouble(businessId));
                    } else {
                        tableIdField.set(obj, businessId);
                    }
                    baseMapper.updateById(obj);
                } else {
                    baseMapper.update(obj, new UpdateWrapper<>().eq(processInstanceId, processId));
                }
            }
        }
    }


    public JuelExpression getWf_status() {
        return wf_status;
    }

    public void setWf_status(JuelExpression wf_status) {
        this.wf_status = wf_status;
    }

    public JuelExpression getWf_back() {
        return wf_back;
    }

    public void setWf_back(JuelExpression wf_back) {
        this.wf_back = wf_back;
    }
}
